// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"
#include "userprog.h"
#include <stdio.h>
#include <iostream>
#define USE_RPC

using namespace std;
extern "C" { int bzero(char *, int); };


#ifndef NETWORK
int DeleteLock_Syscall(int id);
int DeleteCV_Syscall(int id);
#endif
int copyin(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes from the current thread's virtual address vaddr.
    // Return the number of bytes so read, or -1 if an error occors.
    // Errors can generally mean a bad virtual address was passed in.
    // if len exceed the max length for buf, return error
    if (len > MAXFILENAME) {
        return -1;
    }
    bool result;
    int n=0;			// The number of bytes copied in
    int *paddr = new int;

    while ( n >= 0 && n < len) {
      result = machine->ReadMem( vaddr, 1, paddr );
      while(!result) // FALL 09 CHANGES
	  {
   			result = machine->ReadMem( vaddr, 1, paddr ); // FALL 09 CHANGES: TO HANDLE PAGE FAULT IN THE ReadMem SYS CALL
	  }	
      
      buf[n++] = *paddr;
     
      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    delete paddr;
    return len;
}

int copyout(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes to the current thread's virtual address vaddr.
    // Return the number of bytes so written, or -1 if an error
    // occors.  Errors can generally mean a bad virtual address was
    // passed in.
    bool result;
    int n=0;			// The number of bytes copied in

    while ( n >= 0 && n < len) {
      // Note that we check every byte's address
      result = machine->WriteMem( vaddr, 1, (int)(buf[n++]) );

      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    return n;
}

void Create_Syscall(unsigned int vaddr, int len) {
    // Create the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  No
    // way to return errors, though...
    char *buf = new char[len+1];	// Kernel buffer to put the name in

    if (!buf) return;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Create\n");
	delete buf;
	return;
    }

    buf[len]='\0';

    fileSystem->Create(buf,0);
    delete[] buf;
    return;
}

int Open_Syscall(unsigned int vaddr, int len) {
    // Open the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  If
    // the file is opened successfully, it is put in the address
    // space's file table and an id returned that can find the file
    // later.  If there are any errors, -1 is returned.
    char *buf = new char[len+1];	// Kernel buffer to put the name in
    OpenFile *f;			// The new open file
    int id;				// The openfile id

    if (!buf) {
	printf("%s","Can't allocate kernel buffer in Open\n");
	return -1;
    }

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Open\n");
	delete[] buf;
	return -1;
    }

    buf[len]='\0';

    f = fileSystem->Open(buf);
    delete[] buf;

    if ( f ) {
	if ((id = currentThread->space->fileTable.Put(f)) == -1 )
	    delete f;
	return id;
    }
    else
	return -1;
}

void Write_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one. For disk files, the file is looked
    // up in the current address space's open file table and used as
    // the target of the write.
    
    char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output

    if ( id == ConsoleInput) return;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer for write!\n");
	return;
    } else {
        if ( copyin(vaddr,len,buf) == -1 ) {
	    printf("%s","Bad pointer passed to to write: data not written\n");
	    delete[] buf;
	    return;
	}
    }

    if ( id == ConsoleOutput) {
        if (currentThread->spaceId > 0) {
#ifdef NETWORK
            printf("netname %d , ",netname);
#endif
            printf("process %d : ", currentThread->spaceId);
        }
      for (int ii=0; ii<len; ii++) {
	printf("%c",buf[ii]);
      }

    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    f->Write(buf, len);
	} else {
	    printf("%s","Bad OpenFileId passed to Write\n");
	    len = -1;
	}
    }

    delete[] buf;
}

int Read_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one.    We reuse len as the number of bytes
    // read, which is an unnessecary savings of space.
    char *buf;		// Kernel buffer for input
    OpenFile *f;	// Open file for output

    if ( id == ConsoleOutput) return -1;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer in Read\n");
	return -1;
    }

    if ( id == ConsoleInput) {
      //Reading from the keyboard
      scanf("%s", buf);

      if ( copyout(vaddr, len, buf) == -1 ) {
	printf("%s","Bad pointer passed to Read: data not copied\n");
      }
    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    len = f->Read(buf, len);
	    if ( len > 0 ) {
	        //Read something from the file. Put into user's address space
  	        if ( copyout(vaddr, len, buf) == -1 ) {
		    printf("%s","Bad pointer passed to Read: data not copied\n");
		}
	    }
	} else {
	    printf("%s","Bad OpenFileId passed to Read\n");
	    len = -1;
	}
    }

    delete[] buf;
    return len;
}

void Close_Syscall(int fd) {
    // Close the file associated with id fd.  No error reporting.
    OpenFile *f = (OpenFile *) currentThread->space->fileTable.Remove(fd);

    if ( f ) {
      delete f;
    } else {
      printf("%s","Tried to close an unopen file\n");
    }
}

void fork_thread(int vaddr) {
    DEBUG('a', "InitRegisters pid %d, tid %d\n", currentThread->spaceId, currentThread->threadId);
    //currentThread->space->InitRegisters();
    machine->WriteRegister(PCReg, vaddr);
    machine->WriteRegister(NextPCReg, vaddr+4);
    //call Restorestate function inorder to prevent information loss while context switching.
    //write to the stack register , the starting postion of the stack for this thread.  
    // if the stack addr is (vaddr + ThreadPageNum * PageSize - 16), have to copy the code to new vaddr
    // get the stack pos for the new thread
    currentThread->space->SetPageTable();
    currentThread->space->RestoreState();

    machine->Run();
}
void Fork_Syscall(unsigned int vaddr) {
    lockForThread->Acquire();
    if(0==vaddr||(vaddr%4!=0))
        {
            printf("the function address is not valid\n");
            return;
    }
    // Fork a new thread
    Thread * thread = new Thread("thread");
    // Update the Process Table for Multiprogramming part.
    ProcessTable *pt = &processTable[currentThread->spaceId];
    thread->spaceId = currentThread->spaceId;
    thread->threadId = pt->totalThreadNum;
    ++(pt->totalThreadNum);
    ++(pt->activeThreadNum);
    // Allocate the addrespace to the thread being forked which is essentially current thread's addresspsace because threads share the process addressspace. 
    thread->space = currentThread->space;
    
    thread->Fork(fork_thread, vaddr);
    //fork_thread(vaddr, id);  // int (*fn)(void *), void * arg, unsigned long flags
    
    //++threadSum;
    lockForThread->Release();
}

#ifdef NETWORK
// ping client
void Ping(int arg) {
  while (1) {
    RpcReqMessage rpcReq;
    waitForRequst(&rpcReq);
  }
}
#endif

void exec_thread(int vaddr) {
    // Write PCReg, NextPCReg and StackReg
    currentThread->space->InitRegisters(); 
    // Write the space ID to the register 2.
    machine->WriteRegister(RetAddrReg, currentThread->spaceId);
    currentThread->space->RestoreState();
    DEBUG('t', "exec_thread \n");
    machine->Run();
}

void Exec_Syscall(unsigned int vaddr, int len) {
    lockForThread->Acquire();
    // Read the virtual address of the name of the process from the register R4 virtualAddress = machine->ReadRegister(4).
    // Convert it to the physical address and read the contents from there , which will give you the name of the process to be executed.
    char *buf = new char[len+1];
    bzero(buf, len+1);
    if (copyin(vaddr, len, buf) == -1) {
        printf("wrong file name for exec"); 
        delete buf;
        return; 
    }
    // Now Open that file using filesystem->Open.
    // Store its openfile pointer.
    OpenFile *executable = fileSystem->Open(buf);
    
    if (executable == NULL) {
        printf("Unable to open file %s\n", buf);
        return;
    }

    // Create a new thread.
    Thread * thread = new Thread("process");
    // Create new addrespace for this executable file.
    thread->spaceId = processId++;
#ifdef NETWORK
    thread->mailId=currentMail++;
    //thread->haddr = clientinfo->clientHaddr;
    thread->haddr = netname;
    thread->defaultServer=(thread->spaceId-1)%serverNumber;
    // fork ping thread to reply server ping
    Thread * pingThread = new Thread("pingThread");
    pingThread->mailId = currentMail;
    pingThread->Fork(Ping, 0);
    thread->pingId = currentMail;
    ++currentMail;
#endif

    AddrSpace *space = new AddrSpace(executable, thread->spaceId);
    // Allocate the space created to this thread's space.
    thread->space = space;
    //delete executable;
    // Fork the new thread. I call it exec_thread.
    thread->Fork(exec_thread, 0);
    lockForThread->Release();
}
void Exit_Syscall(int status) {
    lockForThread->Acquire();
    if (status != 0) {
        printf("thread exit: error code %d!!!\n", status);
    }
    ProcessTable *pt = &processTable[currentThread->spaceId];
    --(pt->activeThreadNum);
    DEBUG('a', "Exit pid %d, tid %d\n", currentThread->spaceId, currentThread->threadId);
    if (!(pt->activeThreadNum)) {
        int i;
        for (i = 0;i < MAX_PROCESS_NUM; ++i) {
            if (processTable[i].activeThreadNum) {
                break;
            }
        }
        if (i == MAX_PROCESS_NUM) {
            DEBUG('D', "last thread in last process call interrup->Halt exit\n");
            printf("last thread in last process call interrup->Halt exit\n");
            interrupt->Halt();
        }
#ifndef NETWORK
        //delete currentThread->space;
        for (i = 0;i < MAX_KERNEL_LOCK; ++i) {
            if (kernelLock[i].addrSpace == currentThread->space) {
             //  DeleteLock_Syscall(i);
            }
        }
        for (i = 0;i < MAX_KERNEL_CV; ++i) {
            if (kernelCV[i].addrSpace == currentThread->space) {
             //   DeleteCV_Syscall(i);
            }
        }
#endif
#ifdef USE_TLB
        // invalidate ipt
/*
        iptLock->Acquire();
        for (i = 0;i < NumPhysPages; ++i) {
            if (!ipt[i].use && ipt[i].processId == currentThread->spaceId) {
                ipt[i].valid = FALSE;
                paddrTable.Remove(i);
                iptFifoLock->Acquire();
                List * tmpList = new List();
                while (!iptFifoList->IsEmpty()) {
                    int p = int(iptFifoList->Remove()) - 1;
                    if (p == i) {
                        break;
                    }
                    tmpList->Prepend((void*)(p+1));
                }
                while (!tmpList->IsEmpty()) {
                    iptFifoList->Prepend(tmpList->Remove());
                }
                iptFifoLock->Release();
            }
        }
        iptLock->Release();
       */ 
        // remove swaptable
        currentThread->space->lockForPageTable->Acquire(); 
        for (i = 0;i < (int)currentThread->space->numPages; ++i) {
            if (currentThread->space->pageTable[i].pageLocation == swapfd) {
                swapTable->Remove(currentThread->space->pageTable[i].offset/PageSize);
            }
        } 
        currentThread->space->lockForPageTable->Release(); 
#endif
    } else {
        // do it in deamon thread
        //currentThread->space->RemovePageTable();
    }
    lockForThread->Release();
    DEBUG('a', "end Exit pid %d, tid %d\n", currentThread->spaceId, currentThread->threadId);
    currentThread->Finish();
}
#ifndef NETWORK
// create lock: new Lock and put it into lockTable and then set kernalLock 
int CreateLock_Syscall(unsigned int vaddr, int len) {
  /*  lockForLock->Acquire();
    char* buf = new char[len+1];
    if (copyin(vaddr, len, buf) == -1) {
        // copyin fails
        printf("%s","Lock name read fails\n");
        delete buf;
        return -1;
    }

    Lock *lock = new Lock(buf);
    delete buf;
    int i = lockTable.Put(lock);
    if (i == -1) {
        printf("not enough lock for create\n");
        lockForLock->Release();
        return -1;
    }
    kernelLock[i].userSum = 0;
    kernelLock[i].lock = lock;
    kernelLock[i].isToDelete = FALSE;  
    kernelLock[i].addrSpace = currentThread->space;
    kernelLock[i].spaceId = currentThread->spaceId;
    lockForLock->Release();
    return i;*/
    return 1;
}

// acquire lock
int AcquireLock_Syscall(unsigned int vaddr, int len) {
    /*lockForLock->Acquire();
    void * i = lockTable.Get(id);
    if (int(i) == 0) {
        printf("no such lock\n");
        lockForLock->Release();
        return -1;
    }

    if (kernelLock[id].addrSpace != currentThread->space) {
        printf("Fail to Acquire Lock: Can't access other process \n");
        DEBUG('D', "kernelLock[id].spaceId: %d, currentThread->spaceId %d\n", kernelLock[id].spaceId, currentThread->spaceId);
		lockForLock->Release();
        return -1;
    }
    ++(kernelLock[id].userSum);
    lockForLock->Release();
    // Update the Process Table for Multiprogramming part.
    lockForThread->Acquire();
    ProcessTable *pt = &processTable[currentThread->spaceId];
    //--(pt->activeThreadNum);
    lockForThread->Release();

    kernelLock[id].lock->Acquire();

    lockForThread->Acquire();
    //++(pt->activeThreadNum);
    lockForThread->Release();*/
    return 1;
}

int ReleaseLock_Syscall(unsigned int vaddr, int len) {
  /*  lockForLock->Acquire();
    void * i = lockTable.Get(id);
    if (int(i) == 0) {
        printf("no such lock\n");
        lockForLock->Release();
        return -1;
    }

    if (kernelLock[id].addrSpace != currentThread->space) {
        printf("Fail to Release Lock: Can't access other process \n");
        DEBUG('D', "kernelLock[id].spaceId: %d, currentThread->spaceId %d\n", kernelLock[id].spaceId, currentThread->spaceId);
		lockForLock->Release();
        return -1;
    }

    --(kernelLock[id].userSum);
    kernelLock[id].lock->Release();
    lockForLock->Release();*/
    return 1;
}
//delete lock: do delete if userSum == 0
int DeleteLock_Syscall(unsigned int vaddr, int len) {
 /*  lockForLock->Acquire();
    void * i = lockTable.Get(id);
    if (int(i) == 0) {
        printf("no such lock\n");
        lockForLock->Release();
        return -1;
    }

    if (kernelLock[id].addrSpace != currentThread->space) {
        printf("Fail to Delete Lock: Can't access other process \n");
        lockForLock->Release();
        return -1;
    }

    if (kernelLock[id].userSum != 0) {
        // output: have use
        kernelLock[id].isToDelete = TRUE;
        lockForLock->Release();
        return -1;
    }
    delete kernelLock[id].lock;
    kernelLock[id].addrSpace = NULL;
    kernelLock[id].isToDelete = FALSE;
    lockTable.Remove(id);
    lockForLock->Release();*/
    return 1;
}

int CreateCV_Syscall(unsigned int vaddr, int len) {
   /* lockForCondition->Acquire();
    char* buf = new char[len+1];
    if (copyin(vaddr, len, buf) == -1) {
        // copyin fails
        printf("%s","Condition Variable name read fails\n");
        delete buf;
        return -1;
    }
    Condition *condition = new Condition(buf);
    int i = conditionTable.Put(condition);
    if (i == -1) {
        printf("not enough condition for create\n");
        lockForCondition->Release();
        return -1;
    }
    kernelCV[i].condition = condition;  
    kernelCV[i].isToDelete = FALSE;  
    kernelCV[i].userSum = 0;
    kernelCV[i].addrSpace = currentThread->space;
    kernelCV[i].spaceId = currentThread->spaceId;
    lockForCondition->Release();
    return i;*/
    return 1;
}

int WaitCV_Syscall(unsigned int lockvaddr,int lsize,unsigned int CVvaddr,int cvsize) {
  /*  lockForLock->Acquire();
    void * i = lockTable.Get(lockId);
    if (int(i) == 0) {
        printf("no such lock\n");
        lockForLock->Release();
        return -1;
    }
    lockForLock->Release();

    lockForCondition->Acquire();
    i = conditionTable.Get(CVId);
    if (int(i) == 0) {
        DEBUGINFO('r',"no such condition\n");
        lockForCondition->Release();
        return -1;
    }

    if (kernelLock[lockId].addrSpace != currentThread->space || kernelCV[CVId].addrSpace != currentThread->space) {
        printf("Fail to Wait CV: Can't access other process \n");
        DEBUG('D', "kernelLock[lockId].spaceId: %d, kernelCV[CVId].spaceId: %d, currentThread->spaceId %d \n", kernelLock[lockId].spaceId, kernelCV[CVId].spaceId, currentThread->spaceId);
        lockForCondition->Release();
        return -1;
    }

    // add this user
    ++(kernelCV[CVId].userSum);
    lockForCondition->Release();
    
    // Update the Process Table for Multiprogramming part.
    lockForThread->Acquire();
    ProcessTable *pt = &processTable[currentThread->spaceId];
    //--(pt->activeThreadNum);
    lockForThread->Release();
    // ? get lock for lock and check lock
        
    Lock * lock = kernelLock[lockId].lock;
    kernelCV[CVId].condition->Wait(lock);
    lockForThread->Acquire();
    //++(pt->activeThreadNum);
    lockForThread->Release();
    // remove one user
    lockForCondition->Acquire();
    --(kernelCV[CVId].userSum);
    lockForCondition->Release();*/
    return 1; 
}

int SignalCV_Syscall(unsigned int lockvaddr,int lsize,unsigned int CVvaddr,int cvsize) {
  /*  lockForLock->Acquire();
    void * i = lockTable.Get(lockId);
    if (int(i) == 0) {
        printf("no such lock\n");
        lockForLock->Release();
        return -1;
    }
    lockForLock->Release();

    lockForCondition->Acquire();
    i = conditionTable.Get(CVId);
    if (int(i) == 0) {
        DEBUGINFO('r',"no such condition\n");
        lockForCondition->Release();
        return -1;
    }

    if (kernelLock[lockId].addrSpace != currentThread->space || kernelCV[CVId].addrSpace != currentThread->space) {
        printf("Fail to Signal CV: Can't access other process \n");
DEBUG('D', "kernelLock[lockId].spaceId: %d, kernelCV[CVId].spaceId: %d, currentThread->spaceId %d \n", kernelLock[lockId].spaceId, kernelCV[CVId].spaceId, currentThread->spaceId);
        lockForCondition->Release();
        return -1;
    }

    Lock * lock = kernelLock[lockId].lock;
    kernelCV[CVId].condition->Signal(lock);
    lockForCondition->Release();*/
    return 1;
}

int BroadcastCV_Syscall(unsigned int lockvaddr,int lsize,unsigned int CVvaddr,int cvsize) {
  /*  lockForLock->Acquire();
    void * i = lockTable.Get(lockId);
    if (int(i) == 0) {
        printf("no such lock\n");
        lockForLock->Release();
        return -1;
    }
    lockForLock->Release();

    lockForCondition->Acquire();
    i = conditionTable.Get(CVId);
    if (int(i) == 0) {
        DEBUGINFO('r',"no such condition\n");
        lockForCondition->Release();
        return -1;
    }

    if (kernelLock[lockId].addrSpace != currentThread->space || kernelCV[CVId].addrSpace != currentThread->space) {
        printf("Fail to Broadcast CV: Can't access other process \n");
DEBUG('D', "kernelLock[lockId].spaceId: %d, kernelCV[CVId].spaceId: %d, currentThread->spaceId %d \n", kernelLock[lockId].spaceId, kernelCV[CVId].spaceId, currentThread->spaceId);
        lockForCondition->Release();
        return -1;
    }

    Lock * lock = kernelLock[lockId].lock;
    kernelCV[CVId].condition->Broadcast(lock);
    lockForCondition->Release();*/
    return 1;
}

int DeleteCV_Syscall(unsigned int CVvaddr,int cvsize) {
 /*   lockForCondition->Acquire();
    void * i = conditionTable.Get(id);
    if (int(i) == 0) {
        DEBUGINFO('r',"no such condition\n");
        lockForCondition->Release();
        return -1;
    }
    if (kernelCV[id].addrSpace != currentThread->space) {
        printf("Fail: Can't access other process \n");
        DEBUG('D', "kernelCV[CVId].spaceId: %d, currentThread->spaceId %d \n", kernelCV[id].spaceId, currentThread->spaceId);
        lockForCondition->Release();
        return -1;
    }
    if (kernelCV[id].userSum != 0) {
        kernelCV[id].isToDelete = TRUE;
        lockForCondition->Release();
        return 0;
    }
    delete kernelCV[id].condition;
    kernelCV[id].addrSpace = NULL;
    kernelCV[id].isToDelete = FALSE;
    conditionTable.Remove(id);
    lockForCondition->Release();*/
    return 1;
}
#endif
#ifdef NETWORK

// create lock: new Lock and put it into lockTable and then set kernalLock 
int CreateLock_Syscall(unsigned int vaddr, int len) {
    RpcReqMessage rpcReq;
    RpcRplyMessage rpcReply;
    if(len>MAX_NAME){
        // copyin fails
        DEBUGINFO('r',"%s","name too long\n");
        return -1;
    }
    bzero(rpcReq.reqStringParam,MAXREQSTRING);
    if (copyin(vaddr, len, rpcReq.reqStringParam) == -1) {
        // copyin fails
        DEBUGINFO('r',"%s","lock Variable name read fails\n");
        return -1;
    }
    rpcReq.reqType=SC_CreateLock;
    rpcReq.reqIntParam1=len;
    rpcReq.pingId = currentThread->pingId;
    sendRequest(&rpcReq,serversAddr[currentThread->defaultServer],SERVER_MSG_MAIL);
    getReply(&rpcReply);
    if(true==rpcReply.isValid)
        return rpcReply.ansIntParam1;
    else{
         DEBUGINFO('r',"creatLock syscall sent to server, but failed, you need to login in\n");
        return -1;
    };
}

// acquire lock
int AcquireLock_Syscall(unsigned int vaddr, int len) {
    RpcReqMessage rpcReq;
    RpcRplyMessage rpcReply;
   if(len>MAX_NAME){
        // copyin fails
        DEBUGINFO('r',"%s","name too long\n");
        return -1;
    }
    bzero(rpcReq.reqStringParam,MAXREQSTRING);
    if (copyin(vaddr, len, rpcReq.reqStringParam) == -1) {
        // copyin fails
        DEBUGINFO('r',"%s","lock Variable name read fails\n");
        return -1;
    }
    rpcReq.reqType=SC_AcquireLock;
    rpcReq.reqIntParam1=len;
    rpcReq.pingId = currentThread->pingId;
    sendRequest(&rpcReq,serversAddr[currentThread->defaultServer],SERVER_MSG_MAIL);
    getReply(&rpcReply);
    if(true==rpcReply.isValid)
        return rpcReply.ansIntParam1;
    else{
        DEBUGINFO('r',"creatLock syscall sent to server, but failed, you need to login in\n");
        return -1;
    }
}

int ReleaseLock_Syscall(unsigned int vaddr, int len) {
    RpcReqMessage rpcReq;
    RpcRplyMessage rpcReply;
    if(len>MAX_NAME){
        // copyin fails
        DEBUGINFO('r',"%s","name too long\n");
        return -1;
    }
    bzero(rpcReq.reqStringParam,MAXREQSTRING);
    if (copyin(vaddr, len, rpcReq.reqStringParam) == -1) {
        // copyin fails
        DEBUGINFO('r',"%s","lock Variable name read fails\n");
        return -1;
    }
    rpcReq.reqType=SC_ReleaseLock;
    rpcReq.reqIntParam1=len;
    rpcReq.pingId = currentThread->pingId;
    sendRequest(&rpcReq,serversAddr[currentThread->defaultServer],SERVER_MSG_MAIL);
    getReply(&rpcReply);
    if(true==rpcReply.isValid)
        return rpcReply.ansIntParam1;
    else{
        DEBUGINFO('r',"creatLock syscall sent to server, but failed, you need to login in\n");
        return -1;
    }
}
//delete lock: do delete if userSum == 0
int DeleteLock_Syscall(unsigned int vaddr, int len) {
    RpcReqMessage rpcReq;
    RpcRplyMessage rpcReply;
    if(len>MAX_NAME){
        // copyin fails
        DEBUGINFO('r',"%s","name too long\n");
        return -1;
    }
    bzero(rpcReq.reqStringParam,MAXREQSTRING);
    if (copyin(vaddr, len, rpcReq.reqStringParam) == -1) {
        // copyin fails
        DEBUGINFO('r',"%s","lock Variable name read fails\n");
        return -1;
    }
    rpcReq.reqType=SC_DeleteLock;
    rpcReq.reqIntParam1=len;
    sendRequest(&rpcReq,serversAddr[currentThread->defaultServer],SERVER_MSG_MAIL);
    getReply(&rpcReply);
    if(true==rpcReply.isValid)
        return rpcReply.ansIntParam1;
    else{
        DEBUGINFO('r',"creatLock syscall sent to server, but failed, you need to login in\n");
        return -1;
    }
}
int CreateCV_Syscall(unsigned int vaddr, int len) {
    RpcReqMessage rpcReq;
    RpcRplyMessage rpcReply;
    if(len>MAX_NAME){
         // copyin fails
         DEBUGINFO('r',"%s","name too long\n");
         return -1;
     }
     bzero(rpcReq.reqStringParam,MAXREQSTRING);
     if (copyin(vaddr, len, rpcReq.reqStringParam) == -1) {
         // copyin fails
         DEBUGINFO('r',"%s","cv Variable name read fails\n");
         return -1;
     }
    rpcReq.reqType=SC_CreateCV;
    rpcReq.reqIntParam1=len;
    sendRequest(&rpcReq,serversAddr[currentThread->defaultServer],SERVER_MSG_MAIL);
    getReply(&rpcReply);
    if(true==rpcReply.isValid)
        return rpcReply.ansIntParam1;
    else{
        printf("creatLock syscall sent to server, but failed, you need to login in\n");
        return -1;
    };
}

int WaitCV_Syscall(unsigned int lockvaddr,int lsize,unsigned int CVvaddr,int cvsize) {
    RpcReqMessage rpcReq;
    RpcRplyMessage rpcReply;
   if(lsize>MAX_NAME){
         // copyin fails
         DEBUGINFO('r',"%s","name too long\n");
         return -1;
     }
    bzero(rpcReq.reqStringParam,MAXREQSTRING);
     if (copyin(lockvaddr, lsize, rpcReq.reqStringParam) == -1) {
         // copyin fails
         DEBUGINFO('r',"%s","lock Variable name read fails\n");
         return -1;
     }

    if(cvsize>MAX_NAME){
         // copyin fails
         DEBUGINFO('r',"%s","name too long\n");
         return -1;
     }
     bzero(rpcReq.reqStringParam1,MAXREQSTRING);
     if (copyin(CVvaddr, cvsize, rpcReq.reqStringParam1) == -1) {
         // copyin fails
         DEBUGINFO('r',"%s","cv Variable name read fails\n");
         return -1;
     }
    rpcReq.reqType=SC_WaitCV;
    rpcReq.reqIntParam1=lsize;
    rpcReq.reqIntParam2=cvsize;
    rpcReq.pingId = currentThread->pingId;
    sendRequest(&rpcReq,serversAddr[currentThread->defaultServer],SERVER_MSG_MAIL);
    getReply(&rpcReply);
    if(true==rpcReply.isValid)
        return rpcReply.ansIntParam1;
    else{
        printf("creatLock syscall sent to server, but failed, you need to login in\n");
        return -1;
    }

}

int SignalCV_Syscall(unsigned int lockvaddr,int lsize,unsigned int CVvaddr,int cvsize) {
  
  RpcReqMessage rpcReq;
  RpcRplyMessage rpcReply;
  if(lsize>MAX_NAME){
        // copyin fails
        DEBUGINFO('r',"%s","name too long\n");
        return -1;
    }
     bzero(rpcReq.reqStringParam,MAXREQSTRING);
    if (copyin(lockvaddr, lsize, rpcReq.reqStringParam) == -1) {
        // copyin fails
        DEBUGINFO('r',"%s","lock Variable name read fails\n");
        return -1;
    }
  
   if(cvsize>MAX_NAME){
        // copyin fails
        DEBUGINFO('r',"%s","name too long\n");
        return -1;
    }
      bzero(rpcReq.reqStringParam1,MAXREQSTRING);
    if (copyin(CVvaddr, cvsize, rpcReq.reqStringParam1) == -1) {
        // copyin fails
        DEBUGINFO('r',"%s","cv Variable name read fails\n");
        return -1;
    }
   rpcReq.reqType=SC_SignalCV;
   rpcReq.reqIntParam1=lsize;
   rpcReq.reqIntParam2=cvsize;
    rpcReq.pingId = currentThread->pingId;

  sendRequest(&rpcReq,serversAddr[currentThread->defaultServer],SERVER_MSG_MAIL);
  getReply(&rpcReply);
  if(true==rpcReply.isValid)
      return rpcReply.ansIntParam1;
  else{
      printf("creatLock syscall sent to server, but failed, you need to login in\n");
      return -1;
  }
}
int BroadcastCV_Syscall(unsigned int lockvaddr,int lsize,unsigned int CVvaddr,int cvsize) {
    RpcReqMessage rpcReq;
  RpcRplyMessage rpcReply;
  if(lsize>MAX_NAME){
        // copyin fails
        DEBUGINFO('r',"%s","name too long\n");
        return -1;
    }
     bzero(rpcReq.reqStringParam,MAXREQSTRING);
    if (copyin(lockvaddr, lsize, rpcReq.reqStringParam) == -1) {
        // copyin fails
        DEBUGINFO('r',"%s","lock Variable name read fails\n");
        return -1;
    }
  
   if(cvsize>MAX_NAME){
        // copyin fails
        DEBUGINFO('r',"%s","name too long\n");
        return -1;
    }
      bzero(rpcReq.reqStringParam1,MAXREQSTRING);
    if (copyin(CVvaddr, cvsize, rpcReq.reqStringParam1) == -1) {
        // copyin fails
        DEBUGINFO('r',"%s","cv Variable name read fails\n");
        return -1;
    }
   rpcReq.reqType=SC_BroadcastCV;
   rpcReq.reqIntParam1=lsize;
   rpcReq.reqIntParam2=cvsize;
  sendRequest(&rpcReq,serversAddr[currentThread->defaultServer],SERVER_MSG_MAIL);
  getReply(&rpcReply);
  if(true==rpcReply.isValid)
      return rpcReply.ansIntParam1;
  else{
      printf("creatLock syscall sent to server, but failed, you need to login in\n");
      return -1;
  }
}

int DeleteCV_Syscall(unsigned int vaddr,int id) {
  RpcReqMessage rpcReq;
  RpcRplyMessage rpcReply;
  rpcReq.reqType=SC_DeleteCV;
  rpcReq.reqIntParam1=id;
  sendRequest(&rpcReq,serversAddr[currentThread->defaultServer],SERVER_MSG_MAIL);
  getReply(&rpcReply);
  if(true==rpcReply.isValid)
      return rpcReply.ansIntParam1;
  else{
      printf("creatLock syscall sent to server, but failed, you need to login in\n");
      return -1;
  }
}
int rpcLoginOn_Syscall(int password) {
    RpcReqMessage rpcReq;
    RpcRplyMessage rpcReply;
    rpcReq.reqType=SCRPC_LOGINON;
    rpcReq.reqIntParam1=password;
    sendRequest(&rpcReq,serversAddr[currentThread->defaultServer],SERVER_MSG_MAIL);
    getReply(&rpcReply);
    if(true==rpcReply.isValid)
        return rpcReply.ansIntParam1;
    else{
         DEBUGINFO('r',"login failed\n");
        return -1;
    };
}

int rpcCreateMV_Syscall(unsigned int vaddr, int len) {
    RpcReqMessage rpcReq;
    RpcRplyMessage rpcReply;
       bzero(rpcReq.reqStringParam,MAXREQSTRING);
    if (copyin(vaddr, len, rpcReq.reqStringParam) == -1) {
        // copyin fails
        DEBUGINFO('r',"%s","MV Variable name read fails\n");
        return -1;
    }
    rpcReq.reqType=SCRPC_CreateMV;
    rpcReq.reqIntParam1=len;
    sendRequest(&rpcReq,serversAddr[currentThread->defaultServer],SERVER_MSG_MAIL);
    getReply(&rpcReply);
    if(true==rpcReply.isValid)
        return rpcReply.ansIntParam1;
    else{
         DEBUGINFO('r',"rpcCreateMV_Syscall syscall sent to server, but failed, you need to login in\n");
        return -1;
    };
}

int rpcGetMV_Syscall(unsigned int vaddr, int len) {
    RpcReqMessage rpcReq;
    RpcRplyMessage rpcReply;
       bzero(rpcReq.reqStringParam,MAXREQSTRING);
      if (copyin(vaddr, len, rpcReq.reqStringParam) == -1) {
        // copyin fails
        DEBUGINFO('r',"%s","MV Variable name read fails\n");
        return -1;
    }
    rpcReq.reqType=SCRPC_GetMV;
    rpcReq.reqIntParam1=len;
    sendRequest(&rpcReq,serversAddr[currentThread->defaultServer],SERVER_MSG_MAIL);
    getReply(&rpcReply);
    if(true==rpcReply.isValid)
        return rpcReply.ansIntParam1;
    else{
         DEBUGINFO('r',"creatLock syscall sent to server, but failed, you need to login in\n");
        return -1;
    };
}

int rpcSetMV_Syscall(unsigned int vaddr, int len, int value) {
    RpcReqMessage rpcReq;
    RpcRplyMessage rpcReply;
       bzero(rpcReq.reqStringParam,MAXREQSTRING);
    if (copyin(vaddr, len, rpcReq.reqStringParam) == -1) {
        // copyin fails
        DEBUGINFO('r',"%s","MV Variable name read fails\n");
        return -1;
    }
    rpcReq.reqType=SCRPC_SetMV;
    rpcReq.reqIntParam1=len;
    rpcReq.reqIntParam2=value;
    sendRequest(&rpcReq,serversAddr[currentThread->defaultServer],SERVER_MSG_MAIL);
    getReply(&rpcReply);
    if(true==rpcReply.isValid)
        return rpcReply.ansIntParam1;
    else{
         DEBUGINFO('r',"setmv syscall sent to server, but failed, you need to login in\n");
        return -1;
    };
}
int rpcDeleteMV_Syscall(unsigned int vaddr, int len){



    RpcReqMessage rpcReq;
    RpcRplyMessage rpcReply;
       bzero(rpcReq.reqStringParam,MAXREQSTRING);
      if (copyin(vaddr, len, rpcReq.reqStringParam) == -1) {
        // copyin fails
        DEBUGINFO('r',"%s","MV Variable name read fails\n");
        return -1;
    }
    rpcReq.reqType=SCRPC_DeleteMV;
    rpcReq.reqIntParam1=len;
    sendRequest(&rpcReq,serversAddr[currentThread->defaultServer],SERVER_MSG_MAIL);
    getReply(&rpcReply);
    if(true==rpcReply.isValid)
        return rpcReply.ansIntParam1;
    else{
         DEBUGINFO('r',"delete syscall sent to server, but failed, you need to login in\n");
        return -1;
    };

}


int rpcCloseServer_Syscall() {
    RpcReqMessage rpcReq;
    RpcRplyMessage rpcReply;
    rpcReq.reqType=SCRPC_CloseSever;
    sendRequest(&rpcReq,serversAddr[currentThread->defaultServer],SERVER_MSG_MAIL);
    getReply(&rpcReply);
    if(true==rpcReply.isValid)
        return rpcReply.ansIntParam1;
    else{
         DEBUGINFO('r',"close sever sent to server, but failed, you need to login in\n");
        return -1;
    };
}

#endif

void Deamon(int argv) {
    int i;
    while (1) {
        for (i = 0;i < 100; ++i) {
            currentThread->Yield();
        }
        lockForThread->Acquire();
        for (i = 0; i < processId; ++i) {
            if (processTable[i].space == NULL) {
                continue;
            }
            DEBUG('D', "DEAMON INFO: process %d , active num: %d\n", i, processTable[i].activeThreadNum);
            if (0 == processTable[i].activeThreadNum) {
                // delete addrspace
     //           delete processTable[i].space;
                // delete locks, conditions
                // do it in exit
            }
        }
        lockForThread->Release();

        lockForLock->Acquire();
        for (i = 0;i < MAX_KERNEL_LOCK; ++i) {
            void * id = lockTable.Get(i);
            if (int(id) == 0) {
                continue;
            }
            if (!kernelLock[i].userSum && kernelLock[i].isToDelete == TRUE) {
                delete kernelLock[i].lock;
                kernelLock[i].addrSpace = NULL;
                lockTable.Remove(i);
                kernelLock[i].isToDelete = FALSE;
            }
        }
        lockForLock->Release();

        lockForCondition->Acquire();
        for (i = 0;i < MAX_KERNEL_CV; ++i) {
            void * id = conditionTable.Get(i);
            if (int(id) == 0) {
                continue;
            }
            if (!kernelCV[i].userSum && kernelCV[i].isToDelete == TRUE) {
                delete kernelCV[i].condition;
                kernelCV[i].addrSpace = NULL;
                conditionTable.Remove(i);
                kernelCV[i].isToDelete = FALSE;
            }
            
        }
        lockForCondition->Release();
    }
    Exit(0);
}
// tlb page fault handler
#ifdef USE_TLB
void PageFaultHandler(int badVaddr);
int swapOut(TlEntry* te);
//void SetIpt(TranslationEntry te, int spaceId, AddrSpace * as);
void SetIpt(int pid, int vpn, int spaceId, AddrSpace * as);
#endif

void ExceptionHandler(ExceptionType which) {
    int type = machine->ReadRegister(2); // Which syscall?
    int rv=0; 	// the return value from a syscall
    if ( which == SyscallException ) {
	switch (type) {
	    default:
		DEBUG('b', "Unknown syscall - shutting down.\n");
	    case SC_Halt:
		DEBUG('b', "Shutdown, initiated by user program.\n");
		interrupt->Halt();
		break;
	    case SC_Create:
		DEBUG('b', "Create syscall.\n");
		Create_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
	    case SC_Open:
		DEBUG('b', "Open syscall.\n");
		rv = Open_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
	    case SC_Write:
		DEBUG('b', "Write syscall.\n");
		Write_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      machine->ReadRegister(6));
		break;
	    case SC_Read:
		DEBUG('b', "Read syscall.\n");
		rv = Read_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      machine->ReadRegister(6));
		break;
            case SC_Close:
                DEBUG('b', "Close syscall.\n");
                Close_Syscall(machine->ReadRegister(4));
                break;
            case SC_CreateLock:
                DEBUG('b', "CreateLock syscall.\n");
                rv = CreateLock_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
                break;
            case SC_AcquireLock:
                DEBUG('b', "AcquireLock syscall. pid : %d, tid : %d, lockId : %d.\n", currentThread->spaceId, currentThread->threadId, machine->ReadRegister(4));
                rv = AcquireLock_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
                break;
            case SC_ReleaseLock:
                DEBUG('b', "ReleaseLock syscall. pid : %d, tid : %d, lockId : %d.\n", currentThread->spaceId, currentThread->threadId, machine->ReadRegister(4));
                rv = ReleaseLock_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
                break;
            case SC_DeleteLock:
                DEBUG('b', "DeleteLock syscall. pid : %d, tid : %d, lockId : %d.\n", currentThread->spaceId, currentThread->threadId, machine->ReadRegister(4));
                rv = DeleteLock_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
                break;
            case SC_CreateCV:
                DEBUG('b', "CreateCV syscall. pid : %d, tid : %d.\n", currentThread->spaceId, currentThread->threadId);
                rv = CreateCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
                break;
            case SC_WaitCV:
                DEBUG('b', "WaitCV syscall. pid : %d, tid : %d, lockId : %d, cvID : %d.\n", currentThread->spaceId, currentThread->threadId, machine->ReadRegister(4), machine->ReadRegister(5));
                rv = WaitCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5),machine->ReadRegister(6), machine->ReadRegister(7));
                break;
            case SC_SignalCV:
                DEBUG('b', "SignalCV syscall. pid : %d, tid : %d, lockId : %d, cvID : %d.\n", currentThread->spaceId, currentThread->threadId, machine->ReadRegister(4), machine->ReadRegister(5));
                rv = SignalCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5),machine->ReadRegister(6), machine->ReadRegister(7));
                break;
            case SC_BroadcastCV:
                DEBUG('b', "BroadcastCV syscall. pid : %d, tid : %d, lockId : %d, cvID : %d.\n", currentThread->spaceId, currentThread->threadId, machine->ReadRegister(4), machine->ReadRegister(5));
                rv = BroadcastCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5),machine->ReadRegister(6), machine->ReadRegister(7));
                break;
            case SC_DeleteCV:
                DEBUG('b', "DeleteCV syscall. pid : %d, tid : %d.\n", currentThread->spaceId, currentThread->threadId);
                rv = DeleteCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
                break;
	    case SC_Fork:
		DEBUG('b', "Fork syscall.\n");
		Fork_Syscall(machine->ReadRegister(4));
		break;
            case SC_Yield:
                DEBUG('b', "Yield syscall.\n");
                currentThread->Yield();
                break;
            case SC_Exec:
                DEBUG('b', "Exec syscall.\n");
                Exec_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
                break;
//            case SC_Join:
//                DEBUG('a', "Join syscall.\n");
//                Join_Syscall();
//                break;
            case SC_Exit:
                DEBUG('b', "Exit syscall.\n");
                Exit_Syscall(machine->ReadRegister(4));
                break;
 #ifdef NETWORK
            case SCRPC_LOGINON:
                DEBUG('b', "rpclogin syscall. pid : %d, tid : %d.\n", currentThread->spaceId, currentThread->threadId);
                rv = rpcLoginOn_Syscall(machine->ReadRegister(4));
                break;
            case SCRPC_CreateMV:
                DEBUG('b', "rpccreat mv syscall. pid : %d, tid : %d, lockId : %d, cvID : %d.\n", currentThread->spaceId, currentThread->threadId, machine->ReadRegister(4), machine->ReadRegister(5));
                rv = rpcCreateMV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
                break;
            case SCRPC_GetMV:
                DEBUG('b', "rpcgetmv syscall. pid : %d, tid : %d.\n", currentThread->spaceId, currentThread->threadId);
                rv = rpcGetMV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
                break;
            case SCRPC_SetMV:
                DEBUG('b', "rpcsetmv syscall. pid : %d, tid : %d.\n", currentThread->spaceId, currentThread->threadId);
                rv = rpcSetMV_Syscall(machine->ReadRegister(4),machine->ReadRegister(5),machine->ReadRegister(6));
                break;
            case SCRPC_DeleteMV:
                DEBUG('b', "rpcgetmv syscall. pid : %d, tid : %d.\n", currentThread->spaceId, currentThread->threadId);
                rv = rpcDeleteMV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
                break;
            case SCRPC_CloseSever:
                DEBUG('b', "rpcloseserver syscall. pid : %d, tid : %d.\n", currentThread->spaceId, currentThread->threadId);
                rv = rpcCloseServer_Syscall();
                break;
#endif

	}

	// Put in the return value and increment the PC
	machine->WriteRegister(2,rv);
	machine->WriteRegister(PrevPCReg,machine->ReadRegister(PCReg));
	machine->WriteRegister(PCReg,machine->ReadRegister(NextPCReg));
	machine->WriteRegister(NextPCReg,machine->ReadRegister(PCReg)+4);

        lockForDeamon->Acquire();
        if (!isDeamonInit) {
            Thread * thread = new Thread("deamon");
            //thread->Fork(Deamon, 0);
            isDeamonInit = 1;
        }
        lockForDeamon->Release();
	    return;
#ifdef USE_TLB
    } else if (which == PageFaultException) {
        PageFaultHandler(machine->ReadRegister(BadVAddrReg));
        return;
#endif
    } else {
        cout<<"Unexpected user mode exception - which:"<<which<<"  type:"<< type<<endl;
        interrupt->Halt();
    }
}
#ifdef USE_TLB
void AppendFifo(int pid) {
  iptFifoLock->Acquire();
  iptFifoList->Append((void*)(pid+1));
  iptFifoLock->Release();
}
int RemoveFifo() {
  iptFifoLock->Acquire();
  void * rv = iptFifoList->Remove();
  int pid = -1;
  if (rv != NULL) {
    pid = (int)rv-1;
  }
  iptFifoLock->Release();
  return pid;
}

int EvictIptPage() {
    int pid;
    iptLock->Acquire();
    while (1) {
        if (PFIFO) {
            pid = RemoveFifo();
        } else {
            pid = rand()% NumPhysPages;
            DEBUGINFO('6', "use rand to evict %d ", pid);
        }
        if (pid != -1 && !ipt[pid].use && ipt[pid].valid) {
            ipt[pid].use = TRUE;
            break;
        }
        if (PFIFO && ipt[pid].valid) {
            AppendFifo(pid);
        }
    }

    IptEntry * ie = &ipt[pid];
    if (!ie->valid) { 
        return pid;
    }
    ie->space->lockForPageTable->Acquire();
    iptLock->Release();
////DEBUGINFO('4', "evict ppn %d, vpn %d,\n", pid, ie->virtualPage);
    // if in tlb then copy dirty from tlb to ipt
    IntStatus oldLevel = interrupt->SetLevel(IntOff);
    //if (ie->processId == currentThread->spaceId) {
        for (int j = 0; j< TLBSize; ++j) {
            if (machine->tlb[j].valid && machine->tlb[j].physicalPage == pid) {
//DEBUGINFO('4',"invalidate tlb ppn %d, vpn %d,\n", pid,
                machine->tlb[j].valid = FALSE;
                ie->dirty = machine->tlb[j].dirty;
                break;
            }
        }
    //}
    interrupt->SetLevel(oldLevel);
    // if dirty swap out to swapfile
    if (ie->dirty) {
//DEBUGINFO('4', "ie->vpn %d,", ie->virtualPage);
        if (ie->space->pageTable[ie->virtualPage].pageLocation != swapfd) {
            ie->space->pageTable[ie->virtualPage].offset = PAGENONSWAP;
            ie->space->pageTable[ie->virtualPage].dirty = ie->dirty;
        }
        //ie->valid = FALSE;
// ?use
        swapOut(&ie->space->pageTable[ie->virtualPage]);
    }
    ie->space->pageTable[ie->virtualPage].use = FALSE;
    ie->space->lockForPageTable->Release();

    //printf("physicalPage is full!");
    //interrupt->Halt();
    return pid;
}

int IptMissHandler(int vpn) {
    AddrSpace *space = currentThread->space;
    int pid = paddrTable.Put((void*)currentThread->threadId);
    if (pid == -1) {
        // swap out
        pid = EvictIptPage();
    
/*        IptEntry * ie = &ipt[pid];
DEBUGINFO('4', "evict ppn %d, vpn %d,\n", pid, ie->virtualPage);    
        // if in tlb then copy dirty from tlb to ipt
        IntStatus oldLevel = interrupt->SetLevel(IntOff);
        if (ie->processId == currentThread->spaceId) {
          for (int j = 0; j< TLBSize; ++j) {
            if (machine->tlb[j].valid && machine->tlb[j].physicalPage == pid) {
//DEBUGINFO('4',"invalidate tlb ppn %d, vpn %d,\n", pid, 
                machine->tlb[j].valid = FALSE;
                ie->dirty = machine->tlb[j].dirty;
                break;
            }
          }
        }
        interrupt->SetLevel(oldLevel);
        // if dirty swap out to swapfile
        ie->space->lockForPageTable->Acquire();
        if (ie->dirty) {
//DEBUGINFO('4', "ie->vpn %d,", ie->virtualPage);
            if (!ie->space->pageTable[ie->virtualPage].dirty) {
                ie->space->pageTable[ie->virtualPage].offset = PAGENONSWAP;
                ie->space->pageTable[ie->virtualPage].dirty = ie->dirty;
            }
            //ie->valid = FALSE;
// ?use
            swapOut(&ie->space->pageTable[ie->virtualPage]);
        }
        ie->space->lockForPageTable->Release();
*/
    } else {
        iptLock->Acquire();
        ipt[pid].use = TRUE;
        iptLock->Release();
    }

    iptLock->Acquire();
    space->lockForPageTable->Acquire();
//printf("pageTable[vpn] vp : %d, vpn: %d \n" , pageTable[vpn].virtualPage, vpn);
    if (space->pageTable[vpn].use) {
DEBUGINFO('6', "vpn %d pagetable is in use, cannt use it util it's free", vpn);
        ipt[pid].valid = FALSE;
        ipt[pid].use = FALSE;
        ipt[space->pageTable[vpn].physicalPage].use = TRUE;
        space->lockForPageTable->Release(); 
        iptLock->Release();
        return space->pageTable[vpn].physicalPage; 
    }
    iptLock->Release();
    space->pageTable[vpn].physicalPage = pid;
    space->pageTable[vpn].valid = TRUE;
    int paddr = pid * PageSize;
//printf("pageTable[vpn] vp : %d, vpn: %d , valid : %d\n" , pageTable[vpn].virtualPage, vpn, pageTable[vpn].valid);
    // if read from executable, offset should be init in constructer
    // else offset set when first readin
    if (space->pageTable[vpn].pageLocation != NULL) {
        /*if(swapfd==space->pageTable[vpn].pageLocation){
            DEBUGINFO('4', "get page from swap file: paddr: %d,offset : %d, pid %d, ppn %d, vpn %d, dirty: %d, location: %d ", paddr, space->pageTable[vpn].offset, currentThread->spaceId, paddr/128,vpn, space->
pageTable[vpn].dirty, &(space->pageTable[vpn].pageLocation));}
        else{
            DEBUGINFO('4', "get page from excutable file: paddr: %d,offset : %d, pid %d, ppn %d, vpn %d, dirty: %d, location: %d ", paddr, space->pageTable[vpn].offset, currentThread->spaceId, paddr/128,vpn, space->
pageTable[vpn].dirty, &(space->pageTable[vpn].pageLocation));}
*/        
        swapLock->Acquire();
        space->pageTable[vpn].pageLocation->ReadAt(&(machine->mainMemory[paddr]),
            PageSize, space->pageTable[vpn].offset);
        swapLock->Release();
    }
//DEBUG('4', "put spaceId %d, vpn %d,\n", currentThread->spaceId, vpn);
    //SetIpt(space->pageTable[vpn], currentThread->spaceId, space);
    SetIpt(pid, vpn, currentThread->spaceId, space);
    space->pageTable[vpn].use = TRUE;
    if (PFIFO) {
        AppendFifo(pid);
    }
    space->lockForPageTable->Release();
    //SetIpt(pid, vpn, currentThread->spaceId, space);
     
    return pid;
}
void PageFaultHandler(int badVaddr) {
    int evictFlag = 0;
    //IntStatus oldLevel = interrupt->SetLevel(IntOff);
    iptLock->Acquire();
    int vpn = badVaddr / PageSize;
    int i;
    for (i = 0;i < NumPhysPages; ++i) {
        if (ipt[i].virtualPage == vpn && ipt[i].valid == TRUE && ipt[i].processId == currentThread->spaceId && !ipt[i].use) {
            /*if (ipt[i].use) {
                iptLock->Release();
                return;
            }
*/
            ipt[i].use = true;
            break;
        }
    }
    iptLock->Release();
    if (i == NumPhysPages) {
//        printf("no ipt. Looking for pid : %d, vpn : %d, bva: %d\n", currentThread->spaceId, vpn, badVaddr);
//        interrupt->Halt();
        i = IptMissHandler(vpn);
        evictFlag = 1;
    }
    IntStatus oldLevel = interrupt->SetLevel(IntOff);
    TranslationEntry * tlb = &machine->tlb[nextTlb];
    // set the former tlb to ipt
    if (ipt[tlb->physicalPage].valid && tlb->dirty && tlb->valid) {  // && i != tlb->physicalPage) {
        ipt[tlb->physicalPage].dirty = tlb->dirty; 
    }
    tlb->virtualPage = ipt[i].virtualPage;
    tlb->physicalPage = ipt[i].physicalPage;
    tlb->valid = TRUE;  //ipt[i].valid;
    tlb->readOnly = ipt[i].readOnly;
    tlb->dirty = ipt[i].dirty;
/*
    machine->tlb[nextTlb].virtualPage = vpn;
    DEBUG('b', "AddrSpace::PageFaultHandler %d %d ,nowTlbId : %d, used: %d, pid %d tid %d\n", badVaddr, vpn, nextTlb, machine->tlb[nextTlb].use, currentThread->spaceId, currentThread->threadId); 
    machine->tlb[nextTlb].physicalPage = space->pageTable[vpn].physicalPage;
    machine->tlb[nextTlb].valid = space->pageTable[vpn].valid;
    DEBUG('b', "AddrSpace::tlbPageFaultHandler %d\n", space->pageTable[vpn].valid); 
    machine->tlb[nextTlb].readOnly = space->pageTable[vpn].readOnly;
    machine->tlb[nextTlb].dirty = space->pageTable[vpn].dirty;
*/
    DEBUG('b', "AddrSpace::tlbPageFaultHandler %d %d ,nowTlbId : %d, used: %d, pid %d tid %d. vpn %d, ppn %d, valid :  %d, dirty : %d\n", badVaddr, vpn, nextTlb, machine->tlb[nextTlb].use, currentThread->spaceId, currentThread->threadId, machine->tlb[nextTlb].virtualPage, machine->tlb[nextTlb].physicalPage, machine->tlb[nextTlb].valid, machine->tlb[nextTlb].dirty); 
    nextTlb = (nextTlb+1)%TLBSize;

    interrupt->SetLevel(oldLevel);

    iptLock->Acquire();
    ipt[i].use = FALSE;
    iptLock->Release();

    /*if (PFIFO && evictFlag) {
        AppendFifo(i);
    }
*/
    //iptLock->Release();
}

/*void AddrSpace::IptPageFaultHandler(int vpn) {
   
}
*/
/*void SetIpt(TranslationEntry te, int spaceId, AddrSpace * as) {
    iptLock->Acquire();
    //IntStatus oldLevel = interrupt->SetLevel(IntOff);
    // set ipt
    int pid = te.physicalPage;
    ipt[pid].processId = spaceId;
    ipt[pid].virtualPage = te.virtualPage;
    ipt[pid].physicalPage = pid;
    ipt[pid].valid = te.valid;
    ipt[pid].dirty = te.dirty;
    ipt[pid].readOnly = te.readOnly;
    // only in step 4, because in step 1 SetIpt will be called in the init of exec by others.
    ipt[pid].space = as;
    //interrupt->SetLevel(oldLevel);
    iptLock->Release();
}
*/
void SetIpt(int pid, int vpn, int spaceId, AddrSpace * as) {
    //iptLock->Acquire();
    //IntStatus oldLevel = interrupt->SetLevel(IntOff);
    // set ipt
    ipt[pid].processId = spaceId;
    ipt[pid].virtualPage = vpn;
    ipt[pid].physicalPage = pid;
    ipt[pid].valid = TRUE;
    ipt[pid].dirty = FALSE;
    ipt[pid].readOnly = FALSE;
    // only in step 4, because in step 1 SetIpt will be called in the init of exec by others.
    ipt[pid].space = as;
    //iptLock->Release();
}

/*swap page out to the swapfile
*/
int swapOut(TlEntry* te){
    //step 1:set the swap entry's members
    swapLock->Acquire();
    te->pageLocation=swapfd;
    if(PAGENONSWAP==te->offset){
        int offset = swapTable->Put((void*)te);
        te->offset=(offset)*PageSize;
        if(offset>SWAPPAGENUM){
            printf("the swap file has only %d pages, it is now full\n",SWAPPAGENUM);
            return -1;
        }
    }   
    //step 2: write the content of the page to the swap file, from the main memory
DEBUGINFO('4',"swap out te->vpn %d, te->ppn %d, te->offset %d ", te->virtualPage, te->physicalPage, te->offset); 
    int rv = swapfd->WriteAt(&(machine->mainMemory[te->physicalPage*PageSize]),PageSize, te->offset);  
    swapLock->Release();
    return rv;
}
#endif
